package main

import (
	"fmt"
	"github.com/mcuadros/go-defaults"
	_ "github.com/mcuadros/go-defaults"
	"log"
	"math/rand"
	"sync"
	"time"
)

func commonDefer() {
	if err := recover(); err != any(nil) {
		log.Println(err)
	}
}

type Poolfunc interface {
	exec(runable func())
	//submit(callable func() T) TaskMsg[T]
}

type TaskMsg[T any] struct {
	Value  T
	Status byte // 0 就绪态 1运行态 -1取消态 2已完成
}
type Future[T any] interface {
	//Cancle(mayInterruptifRunning bool) bool
	IsCancelled() bool
	IsDone() bool
	Get() T
	WaitGet(timeout int64)
}

func (t TaskMsg[T]) Cancle(mayInterruptifRunning bool) bool {
	//TODO implement
	defer commonDefer()
	if t.Status != 0 && (t.Status&1) == 0 {
		panic(any("已结束状态不可取消"))
	}
	return true
}

func (t TaskMsg[T]) IsCancelled() bool {
	//TODO implement me
	defer commonDefer()
	return ^(t.Status) == 1
}

func (t TaskMsg[T]) IsDone() bool {
	//TODO implement me
	defer commonDefer()
	return t.Status^2 == 0
}

func (t *TaskMsg[T]) Get() any {
	//TODO implement me
	defer commonDefer()
	// 阻塞等待
	for !t.IsDone() {
		fmt.Println("waiting")
		time.Sleep(3000000000)
	}
	return t.Value
}

func (t TaskMsg[T]) WaitGet(timeout int64) {
	//TODO implement me
	defer commonDefer()
}

type GoRoutinePool struct {
	CoreSize           int                                           `default:3`   // 核心协程数
	MaxSize            int                                           `default:6`   // 最大协程数
	GoRoutineContainer []func(index int)                             `default:nil` // 协程处理容器
	cntIndex           int                                           `default:0`   // 当前协程容器下标
	QueueSize          int                                           `default:10`  // 任务队列最大长度
	taskList           chan func(res *TaskMsg[any])                  `default:nil` // 任务队列
	RefundPolicy       func(func() any, GoRoutinePool) *TaskMsg[any] `default:nil` // 拒绝策略
	finalizedQueue     chan int                                      `default:nil` // 容器释放队列
}

var lock sync.Mutex
var lock1 sync.Mutex
var lock2 sync.Mutex

func (g *GoRoutinePool) exec(runnable func() any) {
	defer commonDefer()
	if g.taskList == nil {
		if g.CoreSize > g.MaxSize {
			panic(any("核心协程数量不能大于最大协程数"))
		}
		lock.Lock()
		if g.taskList == nil {
			g.taskList = make(chan func(*TaskMsg[any]), g.QueueSize)
		}
		lock.Unlock()
	}
	if g.QueueSize < 0 {
		g.QueueSize = 0
	}
	if g.GoRoutineContainer == nil {
		g.GoRoutineContainer = make([]func(int), g.MaxSize, g.MaxSize)
	}
	var goal int
	if len(g.taskList) >= g.QueueSize {
		goal = g.MaxSize
	} else {
		goal = g.CoreSize
		g.taskList <- func(res *TaskMsg[any]) {
			res.Status = 0
			res.Status = 1
			ress := runnable()
			res.Status = 2
			res.Value = ress
		}
	}

	if g.cntIndex < goal {
		g.GoRoutineContainer[g.cntIndex] = func(index int) {
			for len(g.taskList) != 0 {
				var run (func(*TaskMsg[any]))
				if len(g.taskList) != 0 {
					//log.Println(string(len(g.taskList)) + " " + string(g.QueueSize))
					run = (<-g.taskList)
				}
				if run != nil {
					run(&TaskMsg[any]{})
				}
			}
			if g.finalizedQueue == nil {
				lock.Lock()
				if g.finalizedQueue == nil {
					g.finalizedQueue = make(chan int, g.MaxSize)
				}
				lock.Unlock()
			}
			g.finalizedQueue <- index
		}
		go g.GoRoutineContainer[g.cntIndex](g.cntIndex) // 开启协程
		g.cntIndex++
	} else {
		// 当每个Container都被开启过，那么我们走下面的逻辑
		// 通过判断 释放队列长度 来选择是否使用临时线程
		//	获取桶中容器的数量
		caplen := len(g.finalizedQueue)
		if caplen == 0 {
			g.RefundPolicy(runnable, *g)
			return
		}
		// 已经执行完释放了的是caplen，那么没有释放的就是 maxSize - caplen
		runnerLen := g.MaxSize - caplen
		// 如果任务队列满了，caplen = 0 执行拒绝策略
		if len(g.taskList) == g.QueueSize {
			if caplen == 0 {
				g.RefundPolicy(runnable, *g)
				return
			}
			nxtIndex := <-g.finalizedQueue
			go g.GoRoutineContainer[nxtIndex](nxtIndex)
			return
		}
		if runnerLen < g.CoreSize {
			nxtIndex := <-g.finalizedQueue
			go g.GoRoutineContainer[nxtIndex](nxtIndex)
			return
		}
	}
}

func (g *GoRoutinePool) submit(callable func() any) (res *TaskMsg[any]) {
	defer commonDefer()
	res = new(TaskMsg[any])
	if g.taskList == nil {
		if g.CoreSize > g.MaxSize {
			panic(any("核心协程数量不能大于最大协程数"))
		}
		lock.Lock()
		if g.taskList == nil {
			g.taskList = make(chan func(*TaskMsg[any]), g.QueueSize)
		}
		lock.Unlock()
	}
	if g.QueueSize < 0 {
		g.QueueSize = 0
	}
	if g.GoRoutineContainer == nil {
		g.GoRoutineContainer = make([]func(int), g.MaxSize, g.MaxSize)
	}
	var goal int
	if len(g.taskList) >= g.QueueSize {
		goal = g.MaxSize
	} else {
		goal = g.CoreSize
		g.taskList <- func(res *TaskMsg[any]) {
			res.Status = 0
			res.Status = 1
			ress := callable()
			res.Status = 2
			res.Value = ress
		}
	}

	if g.cntIndex < goal {
		g.GoRoutineContainer[g.cntIndex] = func(index int) {
			for len(g.taskList) != 0 {
				var run (func(*TaskMsg[any]))
				if len(g.taskList) != 0 {
					//log.Println(string(len(g.taskList)) + " " + string(g.QueueSize))
					run = (<-g.taskList)
				}
				if run != nil {
					fmt.Printf("%p\n", res)
					run(res)
				}
			}
			if g.finalizedQueue == nil {
				lock.Lock()
				if g.finalizedQueue == nil {
					g.finalizedQueue = make(chan int, g.MaxSize)
				}
				lock.Unlock()
			}
			g.finalizedQueue <- index
		}
		go g.GoRoutineContainer[g.cntIndex](g.cntIndex) // 开启协程
		g.cntIndex++
	} else {
		// 当每个Container都被开启过，那么我们走下面的逻辑
		// 通过判断 释放队列长度 来选择是否使用临时线程
		//	获取桶中容器的数量
		caplen := len(g.finalizedQueue)
		if caplen == 0 {
			return g.RefundPolicy(callable, *g)
		}
		// 已经执行完释放了的是caplen，那么没有释放的就是 maxSize - caplen
		runnerLen := g.MaxSize - caplen
		// 如果任务队列满了，caplen = 0 执行拒绝策略
		if len(g.taskList) == g.QueueSize {
			if caplen == 0 {
				return g.RefundPolicy(callable, *g)
			}
			nxtIndex := <-g.finalizedQueue
			go g.GoRoutineContainer[nxtIndex](nxtIndex)
			return
		}
		if runnerLen < g.CoreSize {
			nxtIndex := <-g.finalizedQueue
			go g.GoRoutineContainer[nxtIndex](nxtIndex)
			return
		}
	}
	return
}
func CreateGoRoutinePool() *GoRoutinePool {
	pool := new(GoRoutinePool)
	defaults.SetDefaults(pool)
	return pool
}
func main() {
	pool := CreateGoRoutinePool()
	pool.MaxSize = 6
	pool.cntIndex = 0
	pool.CoreSize = 3
	pool.QueueSize = 100000
	pool.RefundPolicy = func(f func() any, pool GoRoutinePool) *TaskMsg[any] {
		log.Println(any("协程满了，到本地运行"))
		//f()
		return &TaskMsg[any]{}
	}
	//i := 0
	for {
		res := pool.submit(func() any {
			fmt.Println("test")
			time.Sleep(300)
			return rand.Int()
		})
		fmt.Println(res.Get())
		//fmt.Println(i)
		//time.Sleep(500)
		//i++
	}
}
